#include "sock_utils.h"
#define PRINT_TOKEN(token) printf(#token " is %d\n", token)

int delete_socket(int sockfd, fd_set *master)
{
    close(sockfd);
    FD_CLR(sockfd, master);
    return 0;
}

struct sockaddr_in build_sockaddr_in(socket_t socket) 
{
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(socket.port);
    if (!strcmp(socket.ip_addr, "inaddr_any"))
        addr.sin_addr.s_addr = INADDR_ANY;
    else
        inet_aton(socket.ip_addr, &(addr.sin_addr));
    memset(&(addr.sin_zero), '\0', 8);
    return addr;
}
    

int create_tcp_socket(int domain, int protocol, 
                      const socket_t *local_socket, 
                      const socket_t *remote_socket, int backlog)
{
    int sockfd, yes = 1, errsv;
    struct sockaddr_in local_addr, remote_addr;
    
    if ((sockfd = socket(domain, SOCK_STREAM, protocol)) == -1) {
        errsv = errno;
        printf("Error on socket system call\n Error Number:%d\n", errsv);
        return -1;
    }
   
    //PRINT_TOKEN(sockfd); 

    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
        sizeof(int)) == -1) {
        errsv = errno;
        printf("Error on setsockopt system call\n Error Number:%d\n", errsv);
        return -1;
    }
   
    local_addr = build_sockaddr_in(*local_socket);    
    
    if (bind(sockfd, (struct sockaddr *)&local_addr,
        sizeof(struct sockaddr)) == -1) {
        errsv = errno;
        printf("Error on bind system call\n Error Number:%d\n", errsv);
        return -1;
    }

    if (remote_socket != NULL) {
        remote_addr = build_sockaddr_in(*remote_socket);    
        
        if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1) {
            errsv = errno;
            printf("Error on connect system call\n Error Number:%d\n", errsv);
            return -1;
        }
    } else {
        if (listen(sockfd, backlog) == -1) {
            errsv = errno;
            printf("Error on listen system call\n Error Number:%d\n", errsv);
            return -1;
        }
    }

    return sockfd;
}

int create_udp_socket(int domain, int protocol)
{
    int sockfd, errsv, yes = 1;
    struct sockaddr_in local_addr, remote_addr;
    
    if ((sockfd = socket(domain, SOCK_DGRAM, protocol)) == -1) {
        errsv = errno;
        printf("Error on socket system call\n. Error Number:%d", errsv);
        return -1;
    }
    
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
        sizeof(int)) == -1) {
        errsv = errno;
        printf("Error on setsockopt system call\n. Error Number:%d", errsv);
        return -1;
    }
    
    return sockfd;
}

int connect_to_domain(char *domain, socket_t *local_socket)
{
    int sockfd, rv, errsv;
    struct sockaddr_in local_addr;
    struct addrinfo hints, *servinfo, *p;
    void *addr;
    char ipstr[INET6_ADDRSTRLEN];
    
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;

    if ((rv = getaddrinfo(domain, "http", &hints, &servinfo)) != 0) {
        errsv = errno;
        printf("Error in getaddrinfo system call. Error no:%d\n", errsv);
        exit(1);
    }

    for(p = servinfo; p != NULL; p = p->ai_next) {
        
        struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
        addr = &(ipv4->sin_addr);
        inet_ntop(p->ai_family, addr, ipstr, sizeof(ipstr));
        printf("HTTP Proxy: Resolved domain name %s to address: %s\n", domain, ipstr);
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
            p->ai_protocol)) == -1) {
            perror("socket");
            continue;
        }

        local_addr = build_sockaddr_in(*local_socket);    
        
        if (bind(sockfd, (struct sockaddr *)&local_addr,
            sizeof(struct sockaddr)) == -1) {
            errsv = errno;
            printf("Error on bind system call\n Error Number:%d\n", errsv);
            return -1;
        }
        
        if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("connect");
            continue;
        }

        return sockfd; // if we get here, we must have connected successfully
    }

    printf("HTTP Proxy: Unable to resolve the domain name present in URL\n");
    return -1;
}

int sendall(int sockfd, char *send_buf, int *len)
{
    int total = 0;
    // how many bytes weve sent
    int bytesleft = *len; // how many we have left to send
    int n;
    while(total < *len) {
        n = send(sockfd, send_buf+total, bytesleft, 0);
        if (n == -1) { break; }
        total += n;
        bytesleft -= n;
    }
    *len = total; // return number actually sent here
    return n==-1?-1:0; // return -1 on failure, 0 on success
}

